package cn.biplam.back.sql.type;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;

import cn.biplam.common.wrapper.ErrorMessage;

/**
 * <ul>Number数据元(普通浮点精度)
 * 	<li>精度(1<=precision<=38)
 * 	<li>小数点位数(-8<=scale<=6)
 * </ul>
 * <ul>如果数据范围超出，抛出溢出异常
 * @author 	WangSong
 * @create 	Mar 11, 2009 11:04:35 AM
 * @version 1.0
 */
public class Numeric implements Type,Serializable{

	private static final long serialVersionUID = 2845872155562844950L;
	private static final String sTypeName="NUMBER";
	/**Number数据元最大长度*/
	private static final int NUMBER_MAX_PRECISION=38;
	/**Number数据元最大小数位数*/
	private static final int NUMBER_MAX_SCALE=127;
	/**Number数据元最小小数位数(取整位数)*/
	private static final int NUMBER_MIN_SCALE=-84;

	private boolean bIsNull=true;
	/** 精度 */
	private int iPrecision=16;
	/** 小数点右边数字个数 */
	private int iScale=4;
	
	private String sValue=null;

	/**
	 * 构造方法 创建一个空值(isNull=true)的Number数据元
	 * 			默认精度=16,4
	 */
	public Numeric(){}

	/**
	 * 创建Decimal数据元
	 * @param precision 长度(1<=precision<=38)
	 * @throws ErrorMessage
	 */
	public Numeric(int precision) throws ErrorMessage{
		this.setLength(precision);
	}
	
	/**
	 * 构造方法 创建一个指定长度的空值(isNull=true)的Number数据元,默认精度=(16,4)
	 * @param precision		精度(1<=precision<=38)
	 * @param scale			小数点位数(-84<=scale<=127)
	 * @throws ErrorMessage
	 */
	public Numeric(int precision,int scale) throws ErrorMessage {
		this.setLength(precision);
		this.setScale(scale);
	}

	/**
	 * 构造方法 创建一个指定长度和内容的Number数据元
	 * @param precision		精度(1<=precision<=127)
	 * @param scale			小数点位数(-84<=scale<=127)
	 * @param value			内容为数字的字符串
	 * @throws ErrorMessage
	 */
	public Numeric(int precision,int scale,String value) throws ErrorMessage{
		this.setLength(precision);
		this.setScale(scale);
		this.setValue(value);
	}

	/**
	 * 返回数据元的数据类型的完整描述
	 * @return
	 */
	public String getType() {
		StringBuilder sb=new StringBuilder();
		sb.append(sTypeName);
		sb.append("(");
		sb.append(this.iPrecision);
		if(this.iScale>0){
			sb.append(",");
			sb.append(this.iScale);
		}
		sb.append(")");
		return sb.toString();
	}

	/**
	 * 返回数据元数据类型的关键字
	 * @return
	 */
	@SuppressWarnings("static-access")
	public String getTypeName() {
		return this.sTypeName;
	}

	/**
	 * 获得数据元的值，其内容必须经过初始化/设置
	 * @return
	 * @throws ErrorMessage
	 */
	public String getValue() throws ErrorMessage {
		if(this.bIsNull){
			ErrorMessage eMsg=new ErrorMessage("Number type is empty!");
			throw eMsg;
		}else{
			return this.sValue;
		}
	}

	/**
	 * 查询数据元内容是否为空
	 * @return
	 */
	public boolean isNull() {
		return bIsNull;	
	}

	/**
	 * 返回数据元的长度
	 * @return
	 */
	public int getPercesion() {
		return this.iPrecision;
	}
	
	/**
	 * 	 返回精度 
	 * @return
	 */
	public int getScale(){
		return this.iScale;
	}

	/**
	 * 设置数据元的长度(设置并检查Number类型长度是否符合规范)
	 * @param length		精度/总长度(1<=length<=38)
	 * @throws ErrorMessage
	 */
	public void setLength(int length) throws ErrorMessage {
		
		switch(Integer.signum(length)){
		case -1:
			throw new ErrorMessage("Number type length cannot is negative !");
		case 1:
			if(length>NUMBER_MAX_PRECISION){
				throw new ErrorMessage("Number type length cannot bigger than "+NUMBER_MAX_PRECISION+" !");
			}else{
				this.iPrecision=length;
			}
			break;
		case 0:
			throw new ErrorMessage("Number type length cannot is 0 !");
		default:
			throw new ErrorMessage("Unknow signum!");
		}
	}

	/**
	 * 设置Number数据元的小数位数(设置并检查Number类型小数位数是否符合规范)
	 * @param scale			小数点位数(-8<=scale<=6)
	 */
	public void setScale(int scale) throws ErrorMessage{
		switch(Integer.signum(scale)){
		case -1:
			if((scale*(-1))>this.iPrecision){
				StringBuilder sb=new StringBuilder();
				sb.append("Number type scale(");
				sb.append(scale);
				sb.append(") cannot great precision(");
				sb.append(this.iPrecision);
				sb.append(") !");
				throw new ErrorMessage(sb.toString());
			}else if(scale<NUMBER_MIN_SCALE){
				StringBuilder sb=new StringBuilder();
				sb.append("Number type scale(");
				sb.append(scale);
				sb.append(") cannot less than min scale(");
				sb.append(NUMBER_MIN_SCALE);
				sb.append(") !");
				throw new ErrorMessage(sb.toString());
			}else{
				this.iScale=scale;
			}
			break;
		case 1:
			if(scale>this.iPrecision){
				StringBuilder sb=new StringBuilder();
				sb.append("Number type scale(");
				sb.append(scale);
				sb.append(") cannot great precision(");
				sb.append(this.iPrecision);
				sb.append(") !");
				throw new ErrorMessage(sb.toString());
			}else if(scale>NUMBER_MAX_SCALE){
				StringBuilder sb=new StringBuilder();
				sb.append("Number type scale(");
				sb.append(scale);
				sb.append(") cannot bigger than max scale(");
				sb.append(NUMBER_MAX_SCALE);
				sb.append(") !");
				throw new ErrorMessage(sb.toString());
			}else{
				this.iScale=scale;
			}
			break;
		case 0:
			this.iScale=scale;
			break;
		default:
			throw new ErrorMessage("Unknow signum!");
		}
	}

	/**
	 * 设置数据元的值(不允许传入null)
	 * <ul> 判断值是否有效,无效则抛出异常
	 * 如果iScale小于0，精确到小数点左边iScale位，并四舍五入。然后检验有效位是否 <= iPrecision + |iScale|，如果超出，抛出数据溢出异常
	 * 如果iScale大于0，精确到小数点右边iScale位，并四舍五入。然后检验有效位是否 <= iPrecision，如果超出，抛出数据溢出异常
	 * @param value
	 * @throws ErrorMessage
	 */
	public void setValue(String value) throws ErrorMessage {

		/** 存储值为Java值类型 */
		BigDecimal bigValue=null;

		StringBuilder sb=new StringBuilder();
		/** 判断值是否有效,无效则抛出异常 */
		if(!valueInvalid(value,sb)){
			throw new ErrorMessage(sb.toString());
		}
		/**
		 * 如果iScale小于0，精确到小数点左边iScale位，并四舍五入。然后检验有效位是否 <= iPrecision + |iScale|，如果超出，抛出数据溢出异常
		 * 如果iScale大于0，精确到小数点右边iScale位，并四舍五入。然后检验有效位是否 <= iPrecision，如果超出，抛出数据溢出异常
		 */
		if(this.iScale<=0){//类型为取整
			/** 根据精度将值转换成BigDecimal类型 */
			bigValue=new BigDecimal(value,new MathContext(this.iPrecision-this.iScale));
			BigDecimal divideValue=new BigDecimal(Math.pow(10, Math.abs(this.iScale)));
			/** 小数点向左移动|iScale|位，四舍五入取整 */
			bigValue=bigValue.divide(divideValue,0,BigDecimal.ROUND_HALF_UP);
			/** 小数点向右移动|iScale|位 */
			bigValue=bigValue.movePointRight(Math.abs(this.iScale));
			/** 如果值超过范围抛出异常 */
			if(bigValue.precision()>this.iPrecision-this.iScale){
				sb=new StringBuilder();
				sb.append("The value(");
				sb.append(value);
				sb.append(") is out of bound!");
				throw new ErrorMessage(sb.toString());
			}
		}else{//类型含小数位
			/** 取小数点后iScale位，按四舍五入方式进位 */
			bigValue=new BigDecimal(value,new MathContext(this.iPrecision));
			bigValue=bigValue.setScale(this.iScale,BigDecimal.ROUND_HALF_UP);
			/** 如果四舍五入后值超出范围抛出异常 */
			if(bigValue.precision()>this.iPrecision){
				sb=new StringBuilder();
				sb.append("The value(");
				sb.append(value);
				sb.append(") is out of bound!");
				throw new ErrorMessage(sb.toString());
			}
		}

		this.sValue=bigValue.toString();
		/** 设置空标志 */
		this.bIsNull=false;
	}

	/**
	 * 判断是是否有效
	 * @param value 值
	 * @param sp 
	 * @return
	 */
	private boolean valueInvalid(String value,StringBuilder sp){
		boolean isValid=true;
		/** 空值无效 */
		if(value==null){
			isValid=false;
			sp.append("The String parameter is null !");
		}else{
			String[] aRv=value.split("[.]");
			switch(aRv.length){
			case 1://值无小数
				if(iScale>=0){//如果Scale大于0，值的长度>Precision，值无效
					if(aRv[0].length()>this.iPrecision){
						sp.append("The value(");
						sp.append(value);
						sp.append(") is out of bound !");
						isValid=false;
					}
				}else{//如果Scale小于0,值的长度>Precision+|Scale|，值无效
					if((aRv[0].length())>this.iPrecision-iScale){
						sp.append("The value(");
						sp.append(value);
						sp.append(") is out of bound !");
						isValid=false;
					}
				}
				break;
			case 2://值有小数
				if(iScale>=0){//如果Scale大于0，值的整数部分长度+Scale>Precision,值无效
					if((aRv[0].length()+iScale)>this.iPrecision){
						sp.append("The value(");
						sp.append(value);
						sp.append(") is out of bound !");
						isValid=false;
					}
				}else{//如果Scale小于0,值必须为整数返回异常
					sp.append("The value(");
					sp.append(value);
					sp.append(") is not a integer value !");
					isValid=false;
				}
				break;
			default://小数点多于1个，值无效
				sp.append("The value(");
			sp.append(value);
			sp.append(") is not a valid number value !");
			isValid=false;
			break;
			}
		}
		return isValid;
	}

	/**
	 * 返回SQL语法表示的数据内容的字符串，如果内容为空，则返回0
	 * @return
	 */
	public String toString() {
		if(!this.bIsNull | this.sValue==null){
			return "0";
		}else{
			return this.sValue;
		}
	}
}
